package com.basker.pisces.domain.excel;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;

import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;

import com.basker.pisces.core.io.TempFileSystemResource;
import com.basker.pisces.domain.entity.data.DataEntityInputBeanDescriptor;
import com.basker.pisces.domain.entity.data.DataEntityInputBeanDescriptor.PropertyInputDescriptor;
import com.basker.pisces.domain.entity.data.DataEntityInputBeanDescriptor.ReferencePropertyInputDescriptor;
import com.basker.pisces.domain.entity.meta.Entity;
import com.basker.pisces.domain.property.meta.Property;
import com.basker.pisces.domain.property.meta.ReferenceProperty;
import com.basker.pisces.domain.property.meta.TextProperty;
import com.basker.pisces.excel.CellModel;
import com.basker.pisces.excel.CellStyleModel;
import com.basker.pisces.excel.IWorkBook;
import com.basker.pisces.excel.IWorkbookFactory;
import com.basker.pisces.excel.enums.CellValueType;
import com.basker.pisces.excel.enums.WorkbookType;
import com.basker.pisces.utils.ReflectionUtils;

public class EntityExcelHelper {

    /**
     * 将excel文件中的数据转换成对应的输入对象
     *
     * @param workBook      该对象对应的文件就是{@link #generateImportWorkBook(Entity, Class)}生成的
     * @param entity
     * @param inputPojoType
     * @return
     */
    public static <TInputPojo> List<TInputPojo> convert2InputObjects(IWorkBook workBook, Entity entity,
                                                                     Class<TInputPojo> inputPojoType) {
        // 获取输入类的描述
        DataEntityInputBeanDescriptor descriptor = new DataEntityInputBeanDescriptor(entity, inputPojoType);

        int sheetIndex = 0;
        int totalRowNum = workBook.getLastRowNum(sheetIndex);

        List<TInputPojo> inputObjects = new ArrayList<>(totalRowNum);

        // 第0行是列，数据从第一行开始
        for (int rowIndex = 1; rowIndex <= totalRowNum; rowIndex++) {
            TInputPojo inputObject = ReflectionUtils.createNewInstance(inputPojoType);

            final int row = rowIndex;

            iteratePropertyInputDescriptors(entity, inputPojoType, descriptor, (inputDescriptor, columnIndex) -> {
                Object inputValue = workBook.getCellValue(sheetIndex, row, columnIndex);

                if (inputDescriptor instanceof ReferencePropertyInputDescriptor) {
                    ((ReferencePropertyInputDescriptor) inputDescriptor).setSearchValue(inputObject, inputValue);
                } else {
                    inputDescriptor.setValue(inputObject, inputValue);
                }
            });

            inputObjects.add(inputObject);
        }

        return inputObjects;
    }

    /**
     * 生成指定实体的excel导入模板文件（临时文件），录入字段与inputPojoType定义的字段匹配
     *
     * @param entity
     * @param inputPojoType
     * @return
     */
    public static Resource generateImportFileResource(Entity entity, Class<?> inputPojoType) throws IOException {
        IWorkBook workBook = generateImportWorkBook(entity, inputPojoType);

        String displayName = entity.getDisplayName();
        String fileName = displayName + "-import";

        File tempFile = null;

        try {
            TempFileSystemResource tempFileResource = new TempFileSystemResource(fileName, "xlsx");
            tempFile = tempFileResource.getFile();

            workBook.save(tempFile);

            return tempFileResource;
        } finally {
            if (tempFile != null) {
                tempFile.deleteOnExit();
            }
        }
    }

    /**
     * 生成指定实体的excel对象，录入字段与inputPojoType定义的字段匹配
     *
     * @param entity
     * @param inputPojoType
     * @return
     */
    public static IWorkBook generateImportWorkBook(Entity entity, Class<?> inputPojoType) {
        // 获取输入类的描述
        DataEntityInputBeanDescriptor descriptor = new DataEntityInputBeanDescriptor(entity, inputPojoType);

        // 创建工作簿
        IWorkbookFactory factory = IWorkbookFactory.getInstance();
        IWorkBook workBook = factory.create(WorkbookType.XLSX);

        workBook.addSheet(entity.getDisplayName());

        int sheetIndex = 0;
        int rowIndex = 0;

        iteratePropertyInputDescriptors(entity, inputPojoType, descriptor, (inputDescriptor, columnIndex) -> {
            addHeadCell(workBook, inputDescriptor, null, sheetIndex, rowIndex, columnIndex);
        });

        return workBook;
    }

    private static void addHeadCell(IWorkBook workBook, PropertyInputDescriptor inputDescriptor, String cellValue,
                                    int sheetIndex, int rowIndex, int columnIndex) {
        CellStyleModel styleModel = tryGetCellStyleModel(inputDescriptor);
        if (styleModel != null) {
            workBook.setDefaultColumnStyle(sheetIndex, columnIndex, styleModel);
        }

        if (StringUtils.isEmpty(cellValue)) {
            cellValue = inputDescriptor.getDisplayName();
        }

        CellModel cell = new CellModel(rowIndex, columnIndex, cellValue, CellValueType.String,
                CellStyleModel.TEXT_BOLDFONT_STYLE);
        workBook.setCell(sheetIndex, cell);

        // TODO 需要为不同的列设置类型
    }

    private static void iteratePropertyInputDescriptors(Entity entity,
                                                        Class<?> inputPojoType,
                                                        DataEntityInputBeanDescriptor descriptor,
                                                        BiConsumer<PropertyInputDescriptor, Integer> consumer) {
        int properyIndex = 0;

        for (PropertyInputDescriptor inputDescriptor : descriptor.getPropertyInputDescriptors(inputPojoType)) {
            if (inputDescriptor instanceof ReferencePropertyInputDescriptor) {
                ReferencePropertyInputDescriptor referencePropertyInputDescriptor =
                        (ReferencePropertyInputDescriptor) inputDescriptor;
                // excel导入，对于引用属性，提供检索字段录入，可以是代码名称或者其他字段,但是不支持id录入
                if (!referencePropertyInputDescriptor.supportSearchInput()) {
                    continue;
                }
            }

            consumer.accept(inputDescriptor, properyIndex);
            properyIndex++;
        }
    }

    private static CellStyleModel tryGetCellStyleModel(PropertyInputDescriptor inputDescriptor) {
        Property<?> property = inputDescriptor.getProperty();

        if (property instanceof TextProperty || property instanceof ReferenceProperty) {
            return CellStyleModel.TEXT_STYLE;
        }

        return null;
    }
}
